{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# 协程与任务\n",
    " 本节将简述用于协程与任务的高层级API"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 协程\n",
    "通过 async/await 语法来声明 协程 是编写 asyncio 应用的推荐方式。\n",
    "要真正运行一个协程，asyncio 提供了三种主要机制:"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "outputs": [
    {
     "ename": "RuntimeError",
     "evalue": "asyncio.run() cannot be called from a running event loop",
     "output_type": "error",
     "traceback": [
      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[1;31mRuntimeError\u001B[0m                              Traceback (most recent call last)",
      "Cell \u001B[1;32mIn[1], line 10\u001B[0m\n\u001B[0;32m      7\u001B[0m     \u001B[38;5;28;01mawait\u001B[39;00m asyncio\u001B[38;5;241m.\u001B[39msleep(\u001B[38;5;241m3\u001B[39m)\n\u001B[0;32m      8\u001B[0m     \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124m... World!\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[1;32m---> 10\u001B[0m \u001B[43masyncio\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mrun\u001B[49m\u001B[43m(\u001B[49m\u001B[43mmain\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[43m)\u001B[49m\n",
      "File \u001B[1;32mD:\\dev\\miniconda3\\envs\\python-study\\lib\\asyncio\\runners.py:33\u001B[0m, in \u001B[0;36mrun\u001B[1;34m(main, debug)\u001B[0m\n\u001B[0;32m      9\u001B[0m \u001B[38;5;250m\u001B[39m\u001B[38;5;124;03m\"\"\"Execute the coroutine and return the result.\u001B[39;00m\n\u001B[0;32m     10\u001B[0m \n\u001B[0;32m     11\u001B[0m \u001B[38;5;124;03mThis function runs the passed coroutine, taking care of\u001B[39;00m\n\u001B[1;32m   (...)\u001B[0m\n\u001B[0;32m     30\u001B[0m \u001B[38;5;124;03m    asyncio.run(main())\u001B[39;00m\n\u001B[0;32m     31\u001B[0m \u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[0;32m     32\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m events\u001B[38;5;241m.\u001B[39m_get_running_loop() \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[1;32m---> 33\u001B[0m     \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mRuntimeError\u001B[39;00m(\n\u001B[0;32m     34\u001B[0m         \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124masyncio.run() cannot be called from a running event loop\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m     36\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m coroutines\u001B[38;5;241m.\u001B[39miscoroutine(main):\n\u001B[0;32m     37\u001B[0m     \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mValueError\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124ma coroutine was expected, got \u001B[39m\u001B[38;5;132;01m{!r}\u001B[39;00m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;241m.\u001B[39mformat(main))\n",
      "\u001B[1;31mRuntimeError\u001B[0m: asyncio.run() cannot be called from a running event loop"
     ]
    }
   ],
   "source": [
    "# asyncio.run() 函数用来运行最高层级的入口点main()函数\n",
    "# 注意：此方式不能在Jupyter中运行，因为jupyter已经是一个运行的程序\n",
    "import asyncio\n",
    "\n",
    "async def main():\n",
    "    print(\"Hello...\")\n",
    "    await asyncio.sleep(3)\n",
    "    print(\"... World!\")\n",
    "\n",
    "asyncio.run(main())"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "started at 14:24:35\n",
      "hello\n",
      "world\n",
      "finished at 14:24:38\n"
     ]
    }
   ],
   "source": [
    "# await main() 执行协程对象\n",
    "import asyncio\n",
    "import time\n",
    "\n",
    "async def say_after(delay, what):\n",
    "    await asyncio.sleep(delay)\n",
    "    print(what)\n",
    "\n",
    "async def main():\n",
    "    print(f\"started at {time.strftime('%X')}\")\n",
    "\n",
    "    await say_after(1, \"hello\")\n",
    "    await say_after(2, \"world\")\n",
    "\n",
    "    print(f\"finished at {time.strftime('%X')}\")\n",
    "\n",
    "await main()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "started at 14:39:49\n",
      "hello\n",
      "world\n",
      "finished at 14:39:54\n"
     ]
    }
   ],
   "source": [
    "# asyncio.create_task() 函数用来并发运行作为asyncio任务的多个线程\n",
    "# await task 并发执行任务\n",
    "import asyncio\n",
    "import time\n",
    "\n",
    "async def say_after(delay, what):\n",
    "    await asyncio.sleep(delay)\n",
    "    print(what)\n",
    "\n",
    "async def main():\n",
    "    task1 = asyncio.create_task(say_after(1, 'hello'))\n",
    "    task2 = asyncio.create_task(say_after(2, 'world'))\n",
    "\n",
    "    print(f\"started at {time.strftime('%X')}\")\n",
    "    #等待两个任务都完成\n",
    "    # 并发执行\n",
    "    await task1\n",
    "    await task2\n",
    "    print(f\"finished at {time.strftime('%X')}\")\n",
    "\n",
    "await main()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "module 'asyncio' has no attribute 'TaskGroup'",
     "output_type": "error",
     "traceback": [
      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[1;31mAttributeError\u001B[0m                            Traceback (most recent call last)",
      "Cell \u001B[1;32mIn[4], line 12\u001B[0m\n\u001B[0;32m      8\u001B[0m         \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124mf\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mstarted at \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mtime\u001B[38;5;241m.\u001B[39mstrftime(\u001B[38;5;124m'\u001B[39m\u001B[38;5;132;01m%X\u001B[39;00m\u001B[38;5;124m'\u001B[39m)\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m     10\u001B[0m     \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124mf\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mfinished at \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mtime\u001B[38;5;241m.\u001B[39mstrftime(\u001B[38;5;124m'\u001B[39m\u001B[38;5;132;01m%X\u001B[39;00m\u001B[38;5;124m'\u001B[39m)\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m\"\u001B[39m)\n\u001B[1;32m---> 12\u001B[0m \u001B[38;5;28;01mawait\u001B[39;00m main()\n",
      "Cell \u001B[1;32mIn[4], line 5\u001B[0m, in \u001B[0;36mmain\u001B[1;34m()\u001B[0m\n\u001B[0;32m      4\u001B[0m \u001B[38;5;28;01masync\u001B[39;00m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mmain\u001B[39m():\n\u001B[1;32m----> 5\u001B[0m     \u001B[38;5;28;01masync\u001B[39;00m \u001B[38;5;28;01mwith\u001B[39;00m asyncio\u001B[38;5;241m.\u001B[39mTaskGroup() \u001B[38;5;28;01mas\u001B[39;00m tg:\n\u001B[0;32m      6\u001B[0m         task1 \u001B[38;5;241m=\u001B[39m tg\u001B[38;5;241m.\u001B[39mcreate_task(say_after(\u001B[38;5;241m1\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mhello\u001B[39m\u001B[38;5;124m'\u001B[39m))\n\u001B[0;32m      7\u001B[0m         task2 \u001B[38;5;241m=\u001B[39m tg\u001B[38;5;241m.\u001B[39mcreate_task(say_after(\u001B[38;5;241m2\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mworld\u001B[39m\u001B[38;5;124m'\u001B[39m))\n",
      "\u001B[1;31mAttributeError\u001B[0m: module 'asyncio' has no attribute 'TaskGroup'"
     ]
    }
   ],
   "source": [
    "# TaskGroup\n",
    "# 3.11版本才有\n",
    "import asyncio\n",
    "import time\n",
    "async def main():\n",
    "    async with asyncio.TaskGroup() as tg:\n",
    "        task1 = tg.create_task(say_after(1, 'hello'))\n",
    "        task2 = tg.create_task(say_after(2, 'world'))\n",
    "        print(f\"started at {time.strftime('%X')}\")\n",
    "\n",
    "    print(f\"finished at {time.strftime('%X')}\")\n",
    "\n",
    "await main()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 可等待对象\n",
    "如果一个对象可以在 await 语句中使用， 那么它就是可等待对象。\n",
    "可等待对象有三种主要类型：\n",
    "* 协程\n",
    "* 任务\n",
    "* Future"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "42\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\yzpang\\AppData\\Local\\Temp\\ipykernel_14304\\3287118838.py:8: RuntimeWarning: coroutine 'nested' was never awaited\n",
      "  nested()\n",
      "RuntimeWarning: Enable tracemalloc to get the object allocation traceback\n"
     ]
    }
   ],
   "source": [
    "# 协程属于可等待对象，可以再其他协程中被等待\n",
    "import asyncio\n",
    "\n",
    "async def nested():\n",
    "    return 42\n",
    "\n",
    "async def main():\n",
    "    # 协程对象创建，但是没有await，不会执行\n",
    "    nested()\n",
    "\n",
    "    print(await nested())\n",
    "\n",
    "await main()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "**重要：** 协程可以表示两个紧密联系的概念：\n",
    "* 协程函数： 定义形式为 async def 的函数\n",
    "* 协程对象： 调用协程函数所返回的对象"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "outputs": [],
   "source": [
    "# 任务被用来‘并行的’调度协程\n",
    "import asyncio\n",
    "\n",
    "async def nested():\n",
    "    return 42\n",
    "\n",
    "async def main():\n",
    "    task = asyncio.create_task(nested())\n",
    "\n",
    "    await task\n",
    "\n",
    "await main()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "Future 是一种特殊的低层级可等待对象，表示一个异步操作的最终结果\n",
    "\n",
    "当一个 Future 对象 被等待，这意味着协程将保持等待直到该 Future 对象在其他地方操作完毕。\n",
    "\n",
    "在 asyncio 中需要 Future 对象以便允许通过 async/await 使用基于回调的代码。\n",
    "\n",
    "通常情况下 没有必要 在应用层级的代码中创建 Future 对象。\n",
    "\n",
    "Future 对象有时会由库和某些 asyncio API 暴露给用户，用作可等待对象:"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 运行asyncio程序\n",
    "此函数会运行传入的协程，负责管理 asyncio 事件循环，终结异步生成器，并关闭线程池。\n",
    "\n",
    "当有其他 asyncio 事件循环在同一线程中运行时，此函数不能被调用。"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "import asyncio\n",
    "\n",
    "async def main():\n",
    "    await asyncio.sleep(1)\n",
    "    print('hello')\n",
    "\n",
    "asyncio.run(main() )"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 创建任务"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None\n"
     ]
    }
   ],
   "source": [
    "def fib():\n",
    "    return\n",
    "fib()\n",
    "print(fib())"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 休眠"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "22\n"
     ]
    }
   ],
   "source": [
    "import asyncio\n",
    "from asyncio import Future\n",
    "\n",
    "async def callback(fut: Future):\n",
    "    await asyncio.sleep(3)\n",
    "    fut.set_result(\"22\")\n",
    "\n",
    "async def main():\n",
    "    loop = asyncio.get_running_loop()\n",
    "    fut = loop.create_future()\n",
    "    await loop.create_task(callback(fut))\n",
    "    result = await fut\n",
    "    print(result)\n",
    "\n",
    "await main()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 并发运行任务"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 屏蔽取消操作"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}